`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 输入图像数据流程控制
* - 将行缓冲中的数据推入图像处理流水线
*/



module pixel_save_ctrl_3sdram(
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // frame sync
    input  wire         I_frame_start,
    
    input  wire [5:0]   I_save_port_id     ,
    input  wire [5:0]   I_save_scan_id     ,
    //
    input  wire [1:0]   frame_buf_sel,
    // config
    input  wire [1:0]   I_cfg_scan_mode,
    input  wire         I_cfg_pixel_adj_en, // 逐点色度调整
    input  wire         I_cfg_gap_adj_en,   // 缝隙调整
    input  wire [10:0]  I_cfg_scan_length,
    input  wire [5:0]   I_cfg_scan_max,
    input  wire [5:0]   I_cfg_port_max,
    // pixel coe
    input  wire         I_coe_load_busy, // 正在载入逐点调整系数
    // gap coe
    input  wire         I_gap_coe_busy,

    // line buffer
    output wire         O_pixel_req,   // 请求读取本行数据（顺序读取） line_row_buf 的读
    input  wire [7:0]   I_pixel_data,  // 像素数据           //

    // write pixel
    output wire         O_write_start, // 开始写入
    input  wire         I_write_busy,  // 正在写入
    output wire [20:0]  O_write_addr,  // 写入SDRAM地址
    output wire [5:0]   O_write_len,   // 写入长度，32bitx16（8pixel）数量
    output wire [7:0]   O_write_data,   // 写入数据
    
    output wire         O_save_busy //正在处理
);
//------------------------Parameter----------------------
// fsm
localparam [3:0]
    IDLE  = 0,
    IDLE1 = 1,
    READY = 2,
    WPREP = 3,
    WREQ  = 4,
    WDATA = 5,
    WBUSY = 6,
    LOOP0 = 7;


//------------------------Local signal-------------------
// fsm
reg  [3:0]  state;
reg  [3:0]  next;
reg         write_over;   //写结束
reg  [8:0]  total_count; // 一个区域内的总像素数
reg  [2:0]  pixel_color;
reg  [8:0]  pixel_count; // 一次写的总像素数，小于256
reg  [8:0]  pixel_max;   // 一次写的最大像素数
reg         pixel_req;   //写像素请求

// write pixel
reg  [20:0] write_addr;  //写地址
reg  [8:0]  write_len;   //写长度

//sdram addr
wire [20:0]     I_decode_addr;
reg  [1:0]      frame_id;
reg  [10:0]     row_addr;
reg  [1:0]      bank_addr;
reg  [7:0]      col_addr;

//
reg [8:0]pixel_id;

reg save_ctrl_busy;
always@(posedge I_sclk or negedge I_rst_n)begin
     if (~I_rst_n)
        save_ctrl_busy <= 'd0;
    else if (state==IDLE)
        save_ctrl_busy <= 'd0;
    else
        save_ctrl_busy <= 'd1;
end
assign O_save_busy = save_ctrl_busy;
//------------------------Instantiation------------------

//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else if (I_frame_start)
        state <= IDLE1;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            next = IDLE;
        end

        IDLE1:begin
            next = READY;
        end

        READY: begin
            if (~I_write_busy) begin//正在写入
                if(I_cfg_pixel_adj_en && I_cfg_gap_adj_en)begin
                    if (!I_coe_load_busy && !I_gap_coe_busy)
                        next = WPREP;
                end
                else if(I_cfg_pixel_adj_en )begin
                     if (!I_coe_load_busy)
                        next = WPREP;
                 
                end 
                else if(I_cfg_gap_adj_en)begin
                    if (!I_gap_coe_busy)
                        next = WPREP;
                end
                else 
                    next = WPREP;
            end
        end

        WPREP: begin
            next = WREQ;
        end

        WREQ: begin
            next = WDATA;
        end

        WDATA: begin
            if (write_over)  //一行写结束 行控双开属于 2行
                next = WBUSY; 
            else
                next = WDATA;
        end

        WBUSY: begin
            if (~I_write_busy) //正在写入
                next = LOOP0;
            else
                next = WBUSY;
        end

        LOOP0: begin
            if (total_count > 1'b0)
                next = WPREP;
            else
                next = IDLE;
        end

        default: begin
            next = IDLE;
        end
    endcase
end

// write_over 一行写结束
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_over <= 1'b0;
    else if (pixel_req && pixel_color[1] && pixel_count == 1'b1)
        write_over <= 1'b1;
    else
        write_over <= 1'b0;
end

// total_count 一行的像素实际
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        total_count <= 1'b0;
    else if (state == READY)
        total_count <= I_cfg_scan_length;
    else if (state == WREQ)
        total_count <= total_count - pixel_count;
end

// pixel_color
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_color <= 3'b001;
    else if (state == WPREP)
        pixel_color <= 3'b001;
    else if (pixel_req)
        pixel_color <= {pixel_color[1:0], pixel_color[2]};
end

// pixel_count 一行的像素点的个数
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_count <= 1'b0;
    else if (state == WPREP) begin
        if (total_count >= pixel_max)
            pixel_count <= pixel_max;
        else
            pixel_count <= total_count[8:0];
    end
    else if (pixel_req && pixel_color[2])
        pixel_count <= pixel_count - 1'b1;
end

// pixel_max 一行的最大像素
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        pixel_max <= 1'b0;
    else 
        pixel_max <= 9'd256;
end

// pixel_req 一行数据都为高
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        pixel_req <= 1'b0;
    else if (state == WPREP)
        pixel_req <= 1'b1;
    else if (write_over)
        pixel_req <= 1'b0;
end


assign O_pixel_req = pixel_req;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++write pixel++++++++++++++++++++
assign O_write_start = (state == WREQ);
assign O_write_addr  = write_addr;
assign O_write_len   = write_len[8:3];
assign O_write_data  = I_pixel_data;

// write_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_addr <= 1'b0;
    else if (state == WPREP ) 
        write_addr <= I_decode_addr;  //写地址
    else if (state == LOOP0) begin
        write_addr <= write_addr + 16'h0400;
    end
end

// write_len
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        write_len <= 1'b0;
    else if (state == WPREP) begin
        if (total_count >= pixel_max)
            write_len <= pixel_max;
        else
            write_len <= total_count[7:0] + 3'd7; // 按8的倍数向上取整
    end
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++addr decoder+++++++++++++++++++


//
reg [5:0]scan_id;
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        scan_id <= 'b0;
    else if (state == IDLE1)
        scan_id <= I_save_scan_id;
end
//port_id[4:0]

reg [5:0]port_id; // 
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        port_id <= 'b0;
    else if (state == IDLE1)
        port_id <= I_save_port_id;
     
end
//pixel_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        pixel_id <= 'b0;
    // else if (I_frame_start)
        // pixel_id <= 'b0;
    else 
        pixel_id <= 'b0; //pixel_id 在pixel_write.v 模块中计算
end
//frame_id

always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        frame_id <= 'b0;
    else if(state == IDLE1 || state == IDLE)
        frame_id <= frame_buf_sel; //
end
//col_addr

always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        col_addr <= 1'b0;
    else if (state == READY) begin
           // col_addr <= {pixel_id[5:3], port_id[4:2], 2'd0};
        if(I_cfg_scan_mode==2)
            col_addr <= {pixel_id[6:3], port_id[3:2], 2'd0};
        else
            col_addr <= {pixel_id[5:3], port_id[4:2], 2'd0};
    end
end

// row_addr

always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        row_addr <= 1'b0;
    else if (state == READY)begin
    //  row_addr <= {frame_id, 2'd0, scan_id[4:0], pixel_id[7:6]};
        case(I_cfg_scan_mode)
            0:  row_addr <= {frame_id, 2'd0, scan_id[3:0], pixel_id[8:6]};
            1:  row_addr <= {frame_id, 2'd0, scan_id[4:0], pixel_id[7:6]};
            2:  row_addr <= {frame_id, 2'd0, scan_id[4:0], pixel_id[8:7]};
        endcase
    end
end

// bank_addr

always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        bank_addr <= 1'b0;
    else if (state == READY) begin
            bank_addr <= port_id[1:0];
    end
end

assign I_decode_addr = {bank_addr, row_addr, col_addr};
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++
endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
